package main

import (
	"context"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/aceld/zinx/zconf"
	"github.com/aceld/zinx/ziface"
	"github.com/aceld/zinx/znet"
	"smeb.xyz/pub/websocket-subandpub-cluster/internal/handlerwebsocket/protos"
)

const (
	PING         = 0 // PING
	PONG         = 1 // PONG
	KICKED       = 2 // KICKED OFFINE
	LOGIN        = 3 // REQUEST SUB
	LOGINSUCCESS = 4 // LOGINSUCCESS
	DATA         = 5 // DATA
	SUBSUCCESS   = 6 // SUBSUCCESS
	SUB          = 7 // SUB
)

var LoginSuccess []byte = []byte{0, 0, 0, LOGINSUCCESS, 0, 0, 0, 0}

var SubSuccess []byte = []byte{0, 0, 0, SUBSUCCESS, 0, 0, 0, 0}

var Mutex *sync.Mutex = &sync.Mutex{}

func StartAZinxServer() {

	//1 创建一个server服务

	ZinxServer = znet.NewUserConfServer(&zconf.Config{
		WsPort:           Configs.WsPort,
		Host:             "0.0.0.0",
		Mode:             "websocket",
		Name:             "WsSubPubServer",
		Version:          "V1.0.0",
		WorkerPoolSize:   10,
		LogDir:           "./log",
		LogFile:          "zinx.log",
		MaxConn:          1000,
		MaxWorkerTaskLen: 1024,
		MaxMsgChanLen:    1024,
		MaxPacketSize:    4096,
		TCPPort:          10000,
		KcpPort:          10001,
		HeartbeatMax:     10,
	})

	ZinxServer.SetOnConnStart(
		onConnectionStart,
	)

	ZinxServer.SetOnConnStop(
		onConnectionStop,
	)

	ZinxServer.StartHeartBeatWithOption(time.Second*8, &ziface.HeartBeatOption{
		MakeMsg:          HeartBeatMsg,
		OnRemoteNotAlive: OnRemoteNotAlive,
		Router:           &PongRouter{},
		HeartBeatMsgID:   uint32(PING),
	})

	// 2 登录路由
	ZinxServer.AddRouter(LOGIN, &LoginRouter{})

	// 3 订阅路由
	ZinxServer.AddRouter(SUB, &SubscribleRouter{})

	// 3 订阅路由
	ZinxServer.AddRouter(PONG, &PongRouter{})

	// 启动
	ZinxServer.Start()

	Redis.SAdd(context.Background(), "wsserver", ServerName).Result()
	Redis.Set(context.Background(), ServerName, "0", 0)
}

func onConnectionStart(conn ziface.IConnection) {
	ZapLogger.Info("conn start ..." + conn.GetConnIdStr())
	Redis.Incr(context.Background(), ServerName)
	// 3秒后检查是否有订阅，没有则关闭连接
	go func() {
		time.Sleep(time.Second * 3)
		_, e := conn.GetProperty("identity")
		if e != nil {
			conn.Stop()
		}
	}()
}

func onConnectionStop(conn ziface.IConnection) {
	ZapLogger.Info("conn end ..." + conn.GetConnIdStr())
	Redis.Decr(context.Background(), ServerName)
	ctx := context.Background()
	identity, error := conn.GetProperty("identity")
	member, err := strconv.ParseUint(identity.(string), 10, 64)
	if err != nil {
		ZapLogger.Error(err)
	}
	if error == nil && (identity != interface{}(nil)) {
		Redis.Del(ctx, identity.(string)).Result()

		channels := UserChannels[member]
		for _, channel := range channels {
			// 互斥锁
			Mutex.Lock()
			v, b := ChannelUserSubs[channel]
			if b {
				for i, v1 := range v {
					if v1 == conn.GetConnID() {
						ChannelUserSubs[channel] = append(v[:i], v[i+1:]...)
						if len(ChannelUserSubs[channel]) == 0 {
							Redis.SRem(ctx, channel, ServerName).Result()
							delete(ChannelUserSubs, channel)
						}
						break
					}
				}
			}
			Mutex.Unlock()
		}
		delete(UserChannels, member)
		delete(IdentityConnections, member)

		// js, err := json.Marshal(&protos.Conn{
		// 	Servername: ServerName,
		// 	Connid:     conn.GetConnID(),
		// 	Identity:   identity.(string),
		// 	Localaddr:  conn.LocalAddrString(),
		// 	Remoteaddr: conn.RemoteAddrString(),
		// },
		// )

		// if err != nil {
		// 	ZapLogger.Error(err)
		// }

		// Redis.SRem(ctx, ConnId_Redix_key, js).Result()
	} else {
		ZapLogger.Error("conn.GetProperty(identity)", error)
	}

}

// 用户自定义的心跳检测消息处理方法
func HeartBeatMsg(conn ziface.IConnection) []byte {
	return []byte("Server Ping Msg ")
}

// 用户自定义的远程连接不存活时的处理方法
func OnRemoteNotAlive(conn ziface.IConnection) {
	ZapLogger.Error("OnRemoteNotAlive is Called, connID=", conn.GetConnID(), "remoteAddr = ", conn.RemoteAddr())
	//关闭链接
	conn.Stop()
}

type PongRouter struct {
	znet.BaseRouter
}

func (pongRouter *PongRouter) Handle(request ziface.IRequest) {
	ZapLogger.Info("recv from client Pong; Client Id  = " + request.GetConnection().GetConnIdStr() + " Name are Undefined")
}

type LoginRouter struct {
	znet.BaseRouter
}

func (loginRouter *LoginRouter) Handle(request ziface.IRequest) {
	identity := string(request.GetData())
	member, err := strconv.ParseUint(identity, 10, 64)
	if err != nil {
		ZapLogger.Error(err)
	}
	result, error := Redis.Get(context.Background(), identity).Result()
	ctx := context.Background()
	if error == nil && result != "" {
		// 已经登录，踢下线
		ZapLogger.Info("KickedOut")
		str := strings.Split(result, "-")
		Redis.Del(ctx, identity).Result()

		channels := UserChannels[member]
		for _, channel := range channels {
			// 互斥锁
			Mutex.Lock()
			v, b := ChannelUserSubs[channel]
			if b {
				for i, v1 := range v {
					if v1 == request.GetConnection().GetConnID() {
						ChannelUserSubs[channel] = append(v[:i], v[i+1:]...)
						if len(ChannelUserSubs[channel]) == 0 {
							Redis.SRem(ctx, channel, ServerName).Result()
							delete(ChannelUserSubs, channel)
						}
						break
					}
				}
			}
			Mutex.Unlock()
		}
		delete(UserChannels, member)
		delete(IdentityConnections, member)
		// 已经登录，踢下线
		SendKickedMsg(str[0], &protos.KickedOutRequest{
			ConnectId: str[1],
		})
	}
	Redis.Set(ctx, identity, ServerName+"-"+request.GetConnection().GetConnIdStr(), 0)
	request.GetConnection().SetProperty("identity", identity)
	IdentityConnections[member] = request.GetConnection().GetConnID()
	ZapLogger.Info("LoginSuccess")
	// 登录成功
	request.GetConnection().SendToQueue(LoginSuccess)

	// conn := protos.Conn{
	// 	Servername: ServerName,
	// 	Connid:     request.GetConnection().GetConnID(),
	// 	Identity:   identity,
	// 	Localaddr:  request.GetConnection().LocalAddrString(),
	// 	Remoteaddr: request.GetConnection().RemoteAddrString(),
	// }
	// // 注册端点->redis
	// re, err := Redis.SAdd(ctx, ConnId_Redix_key, conn.String()).Result()
	// if err != nil {
	// 	ZapLogger.Error(err)
	// }
	// ZapLogger.Info(re)

}

type SubscribleRouter struct {
	znet.BaseRouter
}

func (subRouter *SubscribleRouter) Handle(request ziface.IRequest) {
	channels := string(request.GetData())
	identity, err := request.GetConnection().GetProperty("identity")
	if err != nil {
		ZapLogger.Error(err)
		request.GetConnection().Stop()
		return
	}
	member, err := strconv.ParseUint(identity.(string), 10, 64)
	// 订阅
	for _, channel := range strings.Split(channels, ",") {
		// 互斥锁
		Mutex.Lock()
		v, b := ChannelUserSubs[channel]
		if !b {
			ctx := context.Background()
			ChannelUserSubs[channel] = []uint64{}
			ChannelUserSubs[channel] = append(v, request.GetConnection().GetConnID())
			Redis.SAdd(ctx, channel, ServerName).Result()
		} else {
			ChannelUserSubs[channel] = append(v, request.GetConnection().GetConnID())
		}
		Mutex.Unlock()
	}
	UserChannels[member] = strings.Split(channels, ",")
	// 发送订阅成功
	request.GetConnection().SendToQueue(SubSuccess)
}
